package com.prizedraw.prize_draw.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.metadata.Sheet;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.prizedraw.prize_draw.common.lang.Const;
import com.prizedraw.prize_draw.common.lang.Result;
import com.prizedraw.prize_draw.common.sheet.UserUpLoad;
import com.prizedraw.prize_draw.entity.*;
import com.prizedraw.prize_draw.excel.ExcelListener;
import com.prizedraw.prize_draw.mapper.DepartmentMapper;
import com.prizedraw.prize_draw.mapper.DepartmentRoleMapper;
import com.prizedraw.prize_draw.mapper.SysUserMapper;
import com.prizedraw.prize_draw.service.*;
import com.prizedraw.prize_draw.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author czj666
 * @since 2021-09-07
 */
@Service
@Slf4j
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
    @Autowired
    RedisUtil redisUtil;

    @Autowired
    SysRoleService sysRoleService;

    @Autowired
    SysUserMapper sysUserMapper;

    @Autowired
    SysMenuService sysMenuService;

    @Autowired
    SysUserRoleService sysUserRoleService;

    @Autowired
    DepartmentService departmentService;

    @Autowired
    DepartmentRoleService departmentRoleService;

    @Autowired
    UserDepartmentRoleService userDepartmentRoleService;

    @Autowired
    DepartmentRoleMapper departmentRoleMapper;

    @Autowired
    DepartmentMapper departmentMapper;

    @Autowired
    SysUserService sysUserService;

    @Autowired
    BCryptPasswordEncoder passwordEncoder;

    @Override
    public SysUser getByUserId(String userId) {
        return getOne(new QueryWrapper<SysUser>().eq("user_id", userId));
    }

    @Override
    public String getUserAuthorityInfo(Integer userId) {
        SysUser user = this.getById(userId);

        String authority = "";

        // 每次访问非登录页面的url链接，请求头带上jwt，都会进行重新获取当前jwt中用户所拥有的权限，为了减少每次切换进行的频繁访问获取权限，
        // 将其权限设置缓存，但是，这里减少了角色对其查询权限，对只单独的去sys_user_menu查询表中，好像消耗不大，但这里不做修改，依旧采取缓存
        // 记得修改时，清除关联的权限缓存
        if (redisUtil.hasKey("GrantedAuthority:" + user.getId())) {
            authority = (String) redisUtil.get("GrantedAuthority:" + user.getId());
        } else {
            // 查询用户所拥有的menu菜单的perm权限
            List<SysMenu> menus = sysMenuService.list(new QueryWrapper<SysMenu>().inSql("id", "select menu_id from sys_user_menu where user_id = " + user.getId()));
            authority = menus.stream().map(SysMenu::getPermission).collect(Collectors.joining(","));
            log.info("用户ID - {} ---拥有的权限：{}", userId, authority);
            redisUtil.set("GrantedAuthority:" + user.getId(), authority, 60 * 60);


            //不用再从userId去查出关联的用户的code了，直接由于用户是直接对菜单权限的，如同角色对菜单权限，简单处理掉
            //获得角色编码--ROLE_admin,ROLE_normal,sys:user:list,....
            /*List<SysRole> roles = sysRoleService.list(new QueryWrapper<SysRole>()
                    .inSql("id", "select role_id from sys_user_role where user_id = " + userId));
            //把角色拼接一起
            if (roles.size() > 0) {
                String roleCodes = roles.stream().map(r -> "ROLE_" + r.getCode()).collect(Collectors.joining(","));
                authority = roleCodes.concat(",");
            }
            //获取菜单操作编码
            List<Long> menuIds = sysUserMapper.getNavMenuIds(userId);
            if (menuIds.size() > 0) {
                List<SysMenu> menus = sysMenuService.listByIds(menuIds);
                String menuPerms = menus.stream().map(SysMenu::getPermission).collect(Collectors.joining(","));
                authority = authority.concat(menuPerms);
            }*/
        }
        return authority;
    }

    @Override
    public void clearUserAuthorityInfo(Integer userId) {
        redisUtil.del("GrantedAuthority:" + userId);
    }

    /*@Override
    public List<Object> importUserExcel(MultipartFile excel, BaseRowModel rowModel, int headLineNum) throws ExcelException, ExcelNullPointException {
        if (excel == null) {
            log.error("请传入正确的excel格式");
            return null;
        }
        List<Object> list = ExcelUtil.readExcel(excel, rowModel, headLineNum);
        //获取excel全部的部门名并且去重
        LinkedHashSet<String> hashSet = new LinkedHashSet<>();
        //存储每个sysuser对象，判断excel里面用户所在的部门存在之后进行批量插入
        List<SysUser> sysUserList = new ArrayList<>();
        //存储所有部门名称
        List<String> departmentNames = new ArrayList<>();
        //存储插入到user_department_role的对象
        List<UserDepartmentRole> userDepartmentRoles = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            SysUser sysUser = new SysUser();
            //获取每一行的对象
            UserDB userDB = (UserDB) list.get(i);
            String userId = userDB.getUserId();
            String userName = userDB.getUserName();
            String password = userDB.getPassword();
            String deptName = userDB.getDeptName();
            Integer gender = userDB.getGender();
            String phoneNum = userDB.getPhoneNum();
            Date hiredate = userDB.getHiredate();
            System.out.println("userId   "+userId);
            System.out.println("userName   "+userName);
            System.out.println("password   "+password);
            System.out.println("deptName   "+deptName);
            System.out.println("gender   "+gender);
            System.out.println("phoneNum   "+phoneNum);
            System.out.println("hiredate   "+hiredate);
//            //存在空值则抛出异常
            if ((userId == null || userId.equals("")) || (userName == null || userName.equals("")) || (password == null || password.equals("")) || (deptName == null || deptName.equals("")) || (gender == null) || (phoneNum == null || phoneNum.equals("")) || (hiredate == null || hiredate.equals(""))) {
                throw new ExcelNullPointException("表格数据存在空值");
            }
            sysUser.setUserId(userId);
            sysUser.setUserName(userName);
            sysUser.setPassowrd(password);
            sysUser.setGender(Integer.valueOf(gender));
            sysUser.setPhoneNum(phoneNum);
            sysUser.setHiredate(hiredate);
            //保存不重复的部门名
            hashSet.add(deptName);
            sysUserList.add(sysUser);
            //存储每一个部门名用于与user_department_role表里user_id一一对应
            departmentNames.add(deptName);
        }
        //将不重复的部门名的set集合类型转换为list类型，方便遍历
        List<String> departmentnamelist = new ArrayList<>(hashSet);
        //判断不重复的部门列表是否都存在，如果都存在则与原集合长度相同，则不会抛出异常
        if (departmentMapper.testExistDepartment(departmentnamelist).size() != hashSet.size()) {
            throw new ExcelException("表中含有不存在的部门");
        }

        List<Integer> departidlist = departmentMapper.selectAllDepartmentId();
//    查询没有对应departmentid和roleid的departmentid
        List<Integer> selectdepartmentrole = departmentMapper.selectNotDepartmentRoleId(departidlist);
        if (selectdepartmentrole.size() != 0) {
            //批量插入没有对应department和role的departmentid
            departmentMapper.batchInsertRoleDepartmentId(selectdepartmentrole);
        }
        //批量插入sys_user
        sysUserService.saveBatch(sysUserList);
        for (int i = 0; i < sysUserList.size(); i++) {
            UserDepartmentRole userDepartmentRole = new UserDepartmentRole();
            //根据部门名称获取department-role表里面的id,并将其设置到插入UserDepartmentRole对象中
            userDepartmentRole.setDepartmentRoleId(departmentMapper.selectDepartmentRoleId(departmentNames.get(i)));
            //根据插入的sysuser对象根据userId获取sys_user表的id，并将其设置到插入UserDepartmentRole对象中
            userDepartmentRole.setUserId(departmentMapper.getInsertId(sysUserList.get(i).getUserId()));
            userDepartmentRoles.add(userDepartmentRole);
        }
        departmentMapper.batchInsertUserDepartmentRole(userDepartmentRoles);
        return ExcelUtil.readExcel(excel, new UserDB(), headLineNum);
    }
*/

    @Override
    public Result excelImport(MultipartFile excel) throws IOException {
        String filename = excel.getOriginalFilename();
        // 对其上传的文件后缀进行判断是否为excel,其实还得判断类型和大小
        if (StrUtil.endWithIgnoreCase(filename,".xlsx") || StrUtil.endWithIgnoreCase(filename,".xls") ){
            InputStream inputStream = new BufferedInputStream(excel.getInputStream());
            ExcelListener excelListener = new ExcelListener();
            // 实例化ExcelReader,传入输入流和监听器，将其读取的每一条数据进行监听拦截处理,
            // 因为read方法读取之后，返回值为void没有任何操作，只负责读每一条数据，并转为指定类型，所以才用到监听器进行对每条数据进行保存
            ExcelReader excelReader = new ExcelReader(inputStream, null, excelListener);
            // 进去读取输入流对应的表，每读取一条数据，映射封装好，每一条数据都会跳到监听器的invoke方法进行处理
            excelReader.read(new Sheet(1,1,UserUpLoad.class));
            List<UserUpLoad> data = excelListener.getData();

            if (!this.validateExcelData(data)){
                return Result.fail("excel部门字段包含不存在的部门");
            }

            this.saveExcelData(data);

            return Result.succ("");
        } else{
            return Result.fail("不支持文件格式xls或者xlsx以外的格式");
        }
    }

    private boolean validateExcelData(List<UserUpLoad> data){
        // 因为部门和岗位都是可以在后台进行删除，为了验证导入的部门名称是明显错误的(即数据库中不存在该部门名称)，或者防止对应的岗位不存在
        // 其实不判断，没有则进行自动增加也是可以做的，但是为了交互效果，验证效果

        // 获取数据库中所有存在的唯一部门名称
        List<Department> departments = departmentService.list(new QueryWrapper<Department>().select("id","name"));
        List<String> departmentNames = departments.stream().map(department -> department.getName()).collect(Collectors.toList());
        // 获取excel表格读取中所有的部门名称
        Set<String> excelDepartmentNames = data.stream().map(d -> d.getDeptName()).collect(Collectors.toSet());
        // 如果导入的部门在数据库中没有，则直接返回告知部门不存在错误，让其去添加部门后再导入
        if (!departmentNames.containsAll(excelDepartmentNames)){
            return false;
        }
        // 如果导入的部门的都是存在的，则判断是否每个岗位中都存在员工，如果不存在(在部门-岗位关联表中没有该关系)，则自动创建，因为个人理解，每个部门中员工的岗位应该都是基本的
        SysRole baseSysRole = sysRoleService.getOne(new QueryWrapper<SysRole>().eq("name", "员工")); //查询员工id，用于关联表
        departments.stream().forEach(department -> {
            departmentRoleMapper.saveAndIgnoreRepeat(department.getId(),baseSysRole.getId());
        });
        return true;
    }

    private void saveExcelData(List<UserUpLoad> data){
        // 对读取映射的对象封装成要插入数据库的对象，声明一个集合，用于插入
        List<SysUser> sysUsers = new ArrayList<>();
        // 遍历每一个用户对象时，同时查询部门-岗位id,进行对应index保存
        List<Integer> departmentRoleIds = new ArrayList<>();
        // 用于插入后的SysUser的id返回和该部门-岗位id进行关联批量保存
        List<UserDepartmentRole> userDepartmentRoles = new ArrayList<>();
        //查询员工id，用于关联表
        SysRole baseSysRole = sysRoleService.getOne(new QueryWrapper<SysRole>().eq("name", "员工"));

        SysUser sysUser = null;
        for (UserUpLoad userUpLoad : data) {
            sysUser = new SysUser();
            BeanUtil.copyProperties(userUpLoad,sysUser);
            // 随便生成一个不重复的6位数的工号
            String userId = "";
            do {
                userId = RandomUtil.randomNumbers(6);
            }while (sysUserService.count(new QueryWrapper<SysUser>().eq("user_id",userId)) != 0);
            sysUser.setUserId(userId);
            // 设置默认密码
            sysUser.setPassowrd(passwordEncoder.encode(Const.DEFAULT_PASSWORD));
            sysUsers.add(sysUser);
            // 同时顺便去查找对应的部门-员工岗位id时多少
            Integer departmentId = departmentService.getOne(new QueryWrapper<Department>().eq("name", userUpLoad.getDeptName())).getId();
            Integer departmentRoleId = departmentRoleService.getOne(new QueryWrapper<DepartmentRole>().eq("department_id", departmentId).eq("role_id", baseSysRole.getId())).getId();
            departmentRoleIds.add(departmentRoleId);
        }
        // 批量保存用户信息
        sysUserService.saveBatch(sysUsers);

        UserDepartmentRole userDepartmentRole = null;
        for (int i = 0; i < sysUsers.size(); i++) {
            userDepartmentRole = new UserDepartmentRole();
            userDepartmentRole.setUserId(sysUsers.get(i).getId());
            userDepartmentRole.setDepartmentRoleId(departmentRoleIds.get(i));
            userDepartmentRoles.add(userDepartmentRole);
        }
        // 批量保存用户-部门岗位关联信息
        userDepartmentRoleService.saveBatch(userDepartmentRoles);
    }
}
